package cs275.ants.graph;

/** 
 * create a CellStack (LIFO) to hold a list of cell locations  
 * set TotalCells = number of cells in grid  
 * choose a cell at random and call it CurrentCell  
 * set VisitedCells = 1  
   
 * while VisitedCells < TotalCells  
 * find all neighbors of CurrentCell with all walls intact   
 * if one or more found  
 * choose one at random  
 * knock down the wall between it and CurrentCell  
 * push CurrentCell location on the CellStack  
 * make the new cell CurrentCell  
 * add 1 to VisitedCells 
 * else  
 * pop the most recent cell entry off the CellStack  
 * make it CurrentCell 
 * endIf 
 * endWhile  
 */

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import cs275.ants.graph.MazeCell.Wall;
import cs275.ants.graph.antgraph.AntGraph;
import cs275.ants.graph.antgraph.AntGraphEdge;
import cs275.ants.graph.antgraph.AntGraphVertex;

public class Maze {
    
    private int width = 0; 
    private int height = 0;
    
    private MazeCell[][] cells;
	private AntGraph graph;
    
    public Maze(int width, int height, MazeCell[][] cells) {
    	this.width = width; 
    	this.height = height; 
    	this.cells = cells;
    }

    // Visible for testing
    List<MazeCell> getReachableNeighbors(MazeCell cell) {
    	List<MazeCell> neighbors = new ArrayList<MazeCell>();
    	  	
    	// wall: 0: left, 1: up, 2: right, 3: down

    	Wall oppositeWall;
    	int row, col;
    	
    	// left 
    	if (!cell.walls.contains(Wall.LEFT) && cell.column != 0) {
    		oppositeWall = Wall.LEFT.getOpposite();
    		row = cell.row;
    		col = cell.column-1;
    		if (!cells[row][col].walls.contains(oppositeWall)) {
    			neighbors.add(cells[row][col]);
    		}
    	}
    	// up
    	if (!cell.walls.contains(Wall.UP) && cell.row != 0) {
    		oppositeWall = Wall.UP.getOpposite();
    		row = cell.row-1;
    		col = cell.column;
    		if (!cells[row][col].walls.contains(oppositeWall)) {
    			neighbors.add(cells[row][col]);
    		}
    	}
    	// right
    	if (!cell.walls.contains(Wall.RIGHT) && cell.column < width-1) {
    		oppositeWall = Wall.RIGHT.getOpposite();
    		row = cell.row;
    		col = cell.column+1;
    		if (!cells[row][col].walls.contains(oppositeWall)) {
    			neighbors.add(cells[row][col]);
    		}
    	}
    	// down 
    	if (!cell.walls.contains(Wall.DOWN) && cell.row < height-1) {
    		oppositeWall = Wall.DOWN.getOpposite();
    		row = cell.row+1;
    		col = cell.column;
    		if (!cells[row][col].walls.contains(oppositeWall)) {
    			neighbors.add(cells[row][col]);
    		}
    	}
    	return neighbors;
    }
    
    public AntGraph getGraph() {
    	if (graph == null) {
			AntGraphVertex vertices[][] = new AntGraphVertex[height][width];
			graph = new AntGraph();
			// create vertices
			for (int row = 0; row < height; row++) {
				for (int col = 0; col < width; col++) {
					AntGraphVertex vertex = new AntGraphVertex(row, col);
					graph.addVertex(vertex);
					vertices[row][col] = vertex;
				}
			}
			for (int row = 0; row < height; row++) {
				for (int col = 0; col < width; col++) {
					MazeCell currentCell = cells[row][col];
					AntGraphVertex currentVertex = vertices[row][col];
					currentVertex.setFood(currentCell.hasFood());

					List<MazeCell> adjCells = getReachableNeighbors(currentCell);
					ListIterator<MazeCell> iter = adjCells.listIterator();
					while (iter.hasNext()) {
						MazeCell neighborCell = iter.next();
						if (neighborCell.row < currentCell.row
								|| ((neighborCell.row == currentCell.row) && (neighborCell.column < currentCell.column))) {
							if (graph.addEdge(new AntGraphEdge(currentVertex,
									vertices[neighborCell.row][neighborCell.column], 0)) == null) {
								System.out.println("Failed to add edge");
							}
						}
					}
				}
			}
		}
		return graph;
    }

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public MazeCell getCell(int row, int col) {
		return cells[row][col];
	}

	public MazeCell[][] getCells() {
		return cells;
	}
}
